import yzOperand from './yzOperand'

/**
 * 统一封装逻辑运算
 */
export default class YzOperator {
  /**
   * 构造函数
   * @param {*} source 原操作对象
   * @param {*} op 操作符
   * @param {*} target 目标操作对象
   */
  constructor (source, op, target) {
    if (!YzOperator.getSupportOperator().includes(op)) {
      throw new Error(`操作符 ${op} 不受系统支持`)
    }
    if (YzOperator.getLogicOperator().includes(op)) {
      if (!(source.type === 'op' && target.type === 'op')) {
        throw new Error(`逻辑运算符操作符和目标操作对象必须均为操作符`)
      }
    }
    if (!(source instanceof yzOperand && target instanceof yzOperand)) {
      throw new Error(`操作数类型不正确`)
    }
    this.source = source
    this.target = target
    this.op = op
    this.type = 'single'
  }

  static getSupportOperator () {
    // TODO: 需要从后端获取
    return [
      ...YzOperator.getBasicOperator(),
      ...YzOperator.getLogicOperator(),
      ...this.getParentheses()
    ]
  }

  static getBasicOperator () {
    return [
      '>',
      '>=',
      '<',
      '<=',
      '==',
      '!='
    ]
  }

  static getLogicOperator () {
    return [
      '&&',
      '||',
      '!'
    ]
  }

  static getParentheses () {
    return [
      '(',
      ')'
    ]
  }

  static getOpDisplayString (op) {
    const opDisplayString = {
      '||': '或',
      '&&': '并且',
      '>=': '≥',
      '<=': '≤',
      '==': '=',
      '!=': '≠'
    }
    return opDisplayString[op] ?? op
  }

  getDisplayString () {
    return `${this.source.getDisplayString()} ${YzOperator.getOpDisplayString(this.op)} ${this.target.getDisplayString()}`
  }

  getExecJsString () {
    if (YzOperator.getLogicOperator().includes(this.op)) {
      return `() => {return (${this.source.getExecJsString()})() ${this.op} (${this.target.getExecJsString()})()}`
    }
    return `() => {return ${this.source.getExecJsString()} ${this.op} ${this.target.getExecJsString()}}`
  }

  // 树的遍历顺序 左 中 右 先序遍历
  getAllOperators () {
    let result = []
    if (this.source.type === 'op' && this.source.opValue) {
      this.getSubOperators(result, this.source.opValue)
    }
    result.push(this)
    if (this.target.type === 'op' && this.target.opValue) {
      this.getSubOperators(result, this.target.opValue)
    }
    return result
  }

  getSubOperators (result, opValue) {
    if (!opValue) {
      return
    }
    let subOperators = opValue.getAllOperators()
    result.push(...subOperators)
  }

  getAllVariables () {
    let result = []
    if (this.source.type === 'variable') {
      result.push(this.source.opValue)
    } else if (this.source.type === 'op') {
      result.push(...this.source.opValue.getAllVariables())
    }
    if (this.target.type === 'variable') {
      result.push(this.target.opValue)
    } else if (this.target.type === 'op') {
      result.push(...this.target.opValue.getAllVariables())
    }
    return new Array(...new Set(result))
  }
}
